﻿using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Folding;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NFox.Pyeditor.Runtime
{
    public class PythonFoldingStrategy
    {

        class PythonCodeBlock
        {

            public int Start;
            public int End;
            public int Level;
            public PythonCodeBlock(int start, int end, int level)
            {
                Start = start;
                End = end;
                Level = level;
            }

            public override string ToString()
            {
                return $"Start={Start};End={End};Level={Level};";
            }

        }

        class PythonBlock
        {
            public int Start;
            public int End;
            public int StartLine;
            public int EndLine;

            public PythonBlock(int start, int startline)
            {
                Start = start;
                StartLine = startline;
            }

            public void SetEnd(int end, int endline)
            {
                End = end;
                EndLine = endline;
            }

        }


        bool IsQuotationMark(char c)
        {
            const char qmark1 = '\'';
            const char qmark2 = '\"';
            return c == qmark1 || c == qmark2;
        }

        public void UpdateFoldings(FoldingManager manager, TextDocument document)
        {
            int firstErrorOffset;
            IEnumerable<NewFolding> newFoldings = CreateNewFoldings(document, out firstErrorOffset);
            manager.UpdateFoldings(newFoldings, firstErrorOffset);
        }


        public IEnumerable<NewFolding> CreateNewFoldings(TextDocument document, out int firstErrorOffset)
        {
            firstErrorOffset = -1;
            return CreateNewFoldings(document);
        }

        static BlockingCollection<PythonBlock> commentblocks;

        public static bool InComment(int offset, int line)
        {
            lock (commentblocks)
            {
                foreach (var block in commentblocks)
                {
                    if (block.Start < offset && (block.End >= offset || block.StartLine == line))
                        return true;
                }
                return false;
            }
        }

        public IEnumerable<NewFolding> CreateNewFoldings(ITextSource document)
        {

            const char comment = '#';
            const char transfer = '\\';
            const char enter = '\r';
            const char space = ' ';
            const char begine = ':';
            const int spaces = 4;

            List<NewFolding> foldings = new List<NewFolding>();
            Stack<PythonCodeBlock> blocks = new Stack<PythonCodeBlock>();
            BlockingCollection<PythonBlock> cblocks = new BlockingCollection<PythonBlock>();
            PythonBlock cblock = null;

            var doc = (TextDocument)document;

            bool instr = false;
            char qmark = '\'';
            int num = 0;
            int tend = 0;

            for (int i = 0; i < doc.LineCount; i++)
            {

                bool currinstr = instr;

                //遍历每行
                var line = doc.Lines[i];

                //首先获取行首偏移和行尾偏移（非空字符）
                int start = line.Offset;
                while (start < line.EndOffset)
                {
                    if (doc.GetCharAt(start) == space)
                        start++;
                    else
                        break;
                }

                int level = (start - line.Offset) / spaces;

                int end = line.EndOffset - 1;
                while (end > line.Offset)
                {
                    char c = doc.GetCharAt(end);
                    if (c == space || c == enter)
                        end--;
                    else
                        break;
                }

                //如果不是空行
                if (start <= end)
                {

                    for (int j = start; j <= end; j++)
                    {
                        //遍历当前行每字符
                        char c = doc.GetCharAt(j);
                        if (instr)
                        {
                            if (c == qmark)
                            {
                                //判断是否是字符串末尾
                                int k = 1;
                                for (; k < num; k++)
                                {
                                    int m = j + k;
                                    if (m >= doc.TextLength || doc.GetCharAt(m) != qmark)
                                        break;
                                }
                                if (k == num)
                                {
                                    instr = false;
                                    cblock.SetEnd(j, i);
                                    j += num - 1;
                                }
                            }
                            else if (c == transfer)
                            {
                                //忽略转义字符
                                j++;
                            }
                        }
                        else
                        {
                            if (IsQuotationMark(c))
                            {
                                //如果是引号，判断是单引号还是三引号
                                qmark = c;
                                instr = true;
                                cblock = new PythonBlock(j, i);
                                cblocks.Add(cblock);

                                int k = 1;
                                for (; k < 3; k++)
                                {
                                    int m = j + k;
                                    if (m >= doc.TextLength || doc.GetCharAt(m) != qmark)
                                        break;
                                }
                                num = k >= 3 ? 3 : 1;
                                j += num - 1;
                            }
                            else if (c == comment)
                            {

                                //如果是注释符号，重新计算行尾偏移
                                cblock = new PythonBlock(j, i);
                                cblock.SetEnd(end, i);
                                cblocks.Add(cblock);
                                end = j - 1;
                                while (end > line.Offset)
                                {
                                    char cc = doc.GetCharAt(end);
                                    if (cc == space)
                                        end--;
                                    else
                                        break;
                                }
                            }
                        }

                    }

                    tend = end;
                    if (currinstr)
                    {
                        //如果在字符串里，设置堆栈最后行末尾为当前行
                        if (blocks.Count > 0)
                        {
                            var o = blocks.Peek();
                            o.End = end;
                        }
                    }
                    else
                    {

                        if (blocks.Count > 0)
                        {
                            int bend = blocks.Peek().End;
                            while (blocks.Count > 0 && blocks.Peek().Level >= level)
                            {
                                //如果堆栈最后行缩进大于等于当前行，代码块出栈
                                var o = blocks.Pop();
                                foldings.Add(new NewFolding(o.Start, bend + 1));
                            }
                        }

                        if (start <= end)
                        {
                            if (doc.GetCharAt(end) == begine)
                            {
                                //如果当前行最后字符为冒号，入栈
                                blocks.Push(new PythonCodeBlock(end, end, level));
                            }
                            else if (blocks.Count > 0)
                            {
                                //设置堆栈最后行末尾为当前行
                                var o = blocks.Peek();
                                o.End = end;
                            }
                        }
                    }
                }
            }

            commentblocks = cblocks;

            while (blocks.Count > 0)
            {
                //剩余的代码块出栈
                var o = blocks.Pop();
                foldings.Add(new NewFolding(o.Start, tend + 1));
            }
            foldings.Sort((a, b) => a.StartOffset.CompareTo(b.StartOffset));
            return foldings;

        }

    }
}
